/**
 * 通用数据源业务处理方案
 * 
 * mixins:[
 *   dataSource(name, target, {})
 * ]
 * @alias xiufu.wang
 */
import Vue from 'vue'
import { ProConfigProvideMixin } from 'mars-pro/packages/pro-config-provide';
import { objectProperty, isArray, isObject } from 'mars-pro/src/pro/util';
import { get, post, postForm } from 'mars-pro/src/pro/request';
import cloneDeep from 'lodash/cloneDeep';
import adaptSource from '../dataSource/adapt'
import formatSource from '../dataSource/format'

const DEFAULT_OPTION = {
    pageSizeField: 'pageSize',
    currentPageField: 'current',
    totalField: 'total'
}

const httpMethodMap = { get: get, post: post, postForm: postForm }

function getConfigFomEnv(contexts, key, defaultV) {
    if (typeof key !== 'string') {
        return key
    }
    for (let i = 0; i < contexts.length; i++) {
        const res = objectProperty(contexts[i], key, null);
        if (res !== null) {
            return res
        }
    }
    return defaultV
}
const customerCallbackAdapts = []
export function applyCustomerAdapt(cb) {
    customerCallbackAdapts.push(cb)
}
// 用户存储共享数据源
const shareDataSoucesCache = {}
let uuid = 1

class DataSourceContext {
    constructor(name, target, options, vm) {
        this.isDataSource = true
        const that = this
        this.name = name
        this.target = target
        this.options = options
        //初始参数
        this.initParams = options.params || {}
        //用户自定义参数 来自于loadDatas
        this.useParams = null
        this.vm = vm
        // 创建响应式 store
        this.store = new (Vue.extend({
            watch: {
                'forceChange': function () {
                    this.loading = false
                    this.inited = false
                }
            },
            data() {

                return {
                    //原始数据
                    datas: [],
                    // 状态
                    loading: false,
                    inited: false,
                    pagination: {
                        pageSize: 10,
                        total: 0,
                        currentPage: 1
                    },
                    //强制变化数据标记
                    forceChange: 1
                }
            },
            methods: {
                resolveFormDatas() {
                    return formatSource(cloneDeep(this.datas), that.mergeOption, that.vm)
                },
                resolveMergeOPtion() {
                    const globalConfig = objectProperty(that.vm.getConfigProvide(), 'config.api', {})
                    return {
                        ...globalConfig,
                        ...that.options
                    }
                }
            },
            computed: {
                // 格式化数据
                formatDatas() {
                    if (this.forceChange > 0) {
                        return this.resolveFormDatas()
                    }
                },
                mergeOption() {
                    if (this.forceChange > 0) {
                        return this.resolveMergeOPtion()
                    }
                }
            }
        }))

        adaptSource(this, customerCallbackAdapts)
    }

    get lazy() {
        return this.options.lazy === true
    }

    get multiple() {
        return this.options.multiple === true
    }

    get enablePagination() {
        return this.options.enablePagination === true
    }

    //原始数据
    get datas() {
        // 初次调用时
        if (this.inited !== true && this.lazy !== true) {
            this.loadDatas()
        }
        return this.store.datas
    }

    //格式化数据
    get formatDatas() {
        if (this.inited !== true && this.lazy !== true) {
            this.loadDatas()
        }
        return this.store.formatDatas
    }

    //分页数据
    get pagination() {
        const p = this.store.pagination
        let defaultPageSize = (this.store.mergeOption || {}).defaultPageSize;
        if (this.disableDefaultPageSize === true) {
            defaultPageSize = null
        }
        return {
            ...p,
            ...(defaultPageSize ? { pageSize: defaultPageSize } : {})
        }
    }
    //是否忙碌
    get loading() {
        return this.store.loading
    }

    //是否初始化
    get inited() {
        return this.store.inited
    }

    get mergeOption() {
        return this.store.mergeOption
    }
    handleFormatDatas(data, opt) {
        return formatSource(cloneDeep(data), opt, this)
    }
    //构建请求参数
    createRquesetParams() {
        //当前用户参数
        const useParams = this.useParams || {}
        //初始化参数
        let initParams = this.initParams
        //分页参数
        const paginationParams = this.pagination

        const pageSizeField = this.mergeOption.pageSizeField || DEFAULT_OPTION.pageSizeField
        const currentPageField = this.mergeOption.currentPageField || DEFAULT_OPTION.currentPageField

        //解析参数
        let p = getConfigFomEnv([this.vm], initParams, initParams)
        if (typeof p === 'function') {
            p = p({ target: this.target, options: this.mergeOption, extParams: this.useParams })
            // 返回false,则代表取消本次数据源请求
            if (p === false) {
                return false
            }
            initParams = p || {}
        } else {
            initParams = p
        }

        const requestParams = {
            ...(isObject(initParams) ? initParams : {}),
            ...(this.enablePagination === true ? {
                [pageSizeField]: paginationParams.pageSize,
                [currentPageField]: paginationParams.currentPage
            } : {}),
            ...useParams,
            // 临时参数优先级最高
            ...(this.shortParams || {})
        }
        return requestParams
    }

    // 解析数据源
    resolveDataSourceTarget() {
        let requestParams = this.createRquesetParams()
        if (requestParams === false) {
            return false
        }
        let target = this.target
        const { config } = this.vm.getConfigProvide() || {}
        const dictDatas = objectProperty(config, 'dictData', {})
        let dataResult = null
        // 从 vm、字典数据中解析数据源
        target = getConfigFomEnv([this.vm, dictDatas], target, target)
        //尝试从config.getDataByDictType解析
        if (typeof target === 'string') {
            if (config && config.getDataByDictType) {
                target = config.getDataByDictType(target, dictDatas, requestParams) || target
            }
        }

        // 解析结果: 数组
        if (isArray(target)) {
            dataResult = target
        }

        //解析结果:如果是个函数
        if (!dataResult && typeof target === 'function') {
            dataResult = target(this)
        }

        // 解析结果: 来自于post | get | postForm
        if (!dataResult && isObject(target) && target.isRequestActuator === true) {
            dataResult = target.data(requestParams).done()
        }

        //解析结果: url
        if (!dataResult && typeof target === 'string') {
            dataResult = httpMethodMap[this.mergeOption.method || 'post'](target, requestParams).done()
        }

        return Promise.resolve(dataResult || target).then(res => {
            if (res && ('response' in res)) {
                return res.response.data
            }
            if (res && ('err' in res)) {
                return null
            }
            return res
        })
    }
    //解析分页数据
    resolvePaginationData() {
        //当前用户参数
        const useParams = {
            ...(this.useParams || {}),
            ...(this.shortParams || {})
        }
        //原始数据
        const datas = this.store.datas
        const pageSizeField = this.mergeOption.pageSizeField || DEFAULT_OPTION.pageSizeField
        const totalField = this.mergeOption.totalField || DEFAULT_OPTION.totalField
        const currentPageField = this.mergeOption.currentPageField || DEFAULT_OPTION.currentPageField
        //解析分页数据
        let pageSize = undefined
        let currentPage = undefined
        let total = 0

        pageSize = objectProperty(useParams, pageSizeField, objectProperty(datas, pageSizeField, this.pagination.pageSize))
        currentPage = objectProperty(useParams, currentPageField, objectProperty(datas, currentPageField, 1))
        total = objectProperty(useParams, totalField, objectProperty(datas, totalField, 0))

        this.store.pagination = { pageSize, currentPage, total }
    }
    loadDatas(extParams, ignore) {
        //临时查询参数
        this.useParams = extParams
        if (this.pending) {
            this.shortParams = null;
            return this.pendingRequest
        }
        let result = this.resolveDataSourceTarget(extParams)
        //取消本次请求
        if (result === false) {
            this.shortParams = null;
            return
        }
        this.pending = true
        this.pendingRequest = result
        if (!ignore) {
            this.store.loading = true
        }

        //自定义转换处理
        if (this.mergeOption.transformData) {
            const transformData = getConfigFomEnv([this.vm], this.mergeOption.transformData, null)
            //如果存在转换函数
            if (typeof transformData === 'function') {
                result = result.then((r) => transformData(r))
            }
        }
        result.then((r) => {
            if (!ignore) {
                const isFreeze = this.options && this.options.freeze === true
                this.store.datas = isFreeze ? Object.freeze(r) : r
                //解析分页数据
                if (this.enablePagination === true) {
                    this.resolvePaginationData()
                }
            }
            this.store.inited = true
            this.store.loading = false
            this.pending = false
            this.pendingRequest = null;
            //请求结束后,清除临时参数
            this.shortParams = null;
            return r
        })
        this.pendingRequest = result
        return result
    }

    clearDatas(resetData, pagination) {
        this.store.datas = resetData
        if (pagination) {
            this.store.pagination = pagination
        }
    }

    resetDatas(fn, ignore) {
        const pageSizeField = this.mergeOption.pageSizeField || DEFAULT_OPTION.pageSizeField
        const currentPageField = this.mergeOption.currentPageField || DEFAULT_OPTION.currentPageField
        this.pagination.currentPage = 1
        const paginationParams = this.enablePagination ? {
            [pageSizeField]: this.pagination.pageSize,
            [currentPageField]: 1
        } : {}
        return this.loadDatas(typeof fn === 'function' ? fn(paginationParams) : {
            ...paginationParams,
            ...(fn || {})
        }, ignore)
    }

    shortLoadDatas(shortParams, ignore) {
        this.shortParams = shortParams || {};
        return this.loadDatas(this.useParams, ignore)
    }

    getScope() {
        return this.vm
    }

    getPrimitiveData() {
        return this.datas
    }

    updateOption(noptions, ntarget, ignore) {
        this.pending = false
        this.options = noptions || {}
        if (ntarget) {
            this.target = ntarget
        }
        if (noptions && noptions.params && noptions.params !== this.initParams) {
            this.initParams = noptions.params
        }
        if (ignore) {
            return
        }
        this.store.inited = false
        //强制更新
        this.store.forceChange++
    }

    getInited() {
        return this.inited
    }

    // 待扩展
    destroy() {
        this.store.$destroy()
        this.vm = undefined
        this.store = undefined
    }
}

// 返回mixins
export default function (name, target, options) {
    return {
        mixins: [ProConfigProvideMixin],
        beforeCreate() {
            const that = this;
            //单例模式
            this[name] = new DataSourceContext(name, target, options || {}, this)
            // 用于多实例
            const shareDataSoucesCache = this.shareDataSoucesCache || (this.shareDataSoucesCache = {})
            this[name].clone = (key) => {
                const _this = that[name]
                if (key && shareDataSoucesCache[key]) {
                    return shareDataSoucesCache[key]
                }
                key = key || ('datasource-clone-' + (++uuid))
                shareDataSoucesCache[key] = new DataSourceContext(_this.name || name, _this.target || target, _this.options || (options || {}), that)
                return shareDataSoucesCache[key]
            }
        },
        beforeDestroy() {
            const shareDataSoucesCache = this.shareDataSoucesCache || {}
            for (const key in shareDataSoucesCache) {
                shareDataSoucesCache[key].destroy()
            }
            this[name].destroy()
            this.shareDataSoucesCache = undefined
            this[name] = undefined
        }
    }
}

export {
    DataSourceContext
}